package br.com.timecardforpeon;

import static br.com.timecardforpeon.util.Util.dateWithoutHourMinuteSecondAndMillisecond;
import static java.util.Calendar.DAY_OF_MONTH;
import static java.util.Calendar.MILLISECOND;
import static java.util.Calendar.MONTH;
import static java.util.Calendar.SECOND;
import static java.util.Calendar.YEAR;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import br.com.timecardforpeon.adapter.EntryAdapter;
import br.com.timecardforpeon.dialog.ExportDialog;
import br.com.timecardforpeon.dialog.NotesDialog;
import br.com.timecardforpeon.domain.Day;
import br.com.timecardforpeon.domain.Entry;
import br.com.timecardforpeon.event.Event;
import br.com.timecardforpeon.event.EventListener;
import br.com.timecardforpeon.event.EventSubscriber;
import br.com.timecardforpeon.event.impl.RemoveEntryEvent;
import br.com.timecardforpeon.event.impl.UpdateEntryEvent;
import br.com.timecardforpeon.exception.TimeCardException;
import br.com.timecardforpeon.sqlite.domain.DayDao;
import br.com.timecardforpeon.sqlite.domain.EntryDao;
import br.com.timecardforpeon.util.Util;

public class MainActivity extends Activity implements EventListener {

	private DayDao dayDao;
	private EntryDao entryDao;
	private EntryAdapter adapter;
	private Entry openEntry;
	private TextView dayText;
	private ListView entriesListView;
	private Date actualDate;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		this.setContentView(R.layout.main);

		this.actualDate = Util.dateWithoutHourMinuteSecondAndMillisecond(new Date());
		this.entryDao = new EntryDao(this);
		this.dayDao = new DayDao(this);

		this.dayText = Util.<TextView> getViewById(this, R.id.dayText);
		this.entriesListView = Util.<ListView> getViewById(this, R.id.entriesListView);

		this.setScreenDate();
		this.listEntries();

		EventSubscriber.getInstance().addListener(UpdateEntryEvent.class, this).addListener(RemoveEntryEvent.class, this);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {

		MenuInflater inflater = this.getMenuInflater();
		inflater.inflate(R.menu.main_menu, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {

		switch (item.getItemId()) {
		case R.id.todayMenuButton: {
			this.setActualDate(dateWithoutHourMinuteSecondAndMillisecond(new Date()));
			break;
		}
		case R.id.exportButton: {
			this.onClickExport();
			break;
		}
		case R.id.exitMenuButton: {
			this.finish();
			break;
		}
		default: {
			return super.onOptionsItemSelected(item);
		}
		}
		return true;
	}

	private void setScreenDate() {
		this.dayText.setText(Util.dateToString("dd/MM/yyyy", this.actualDate));
	}

	private void listEntries() {
		this.listDateEntries();
		this.listMonthEntries();
	}

	private void listDateEntries() {
		List<Entry> entries = this.entryDao.listByDay(this.actualDate);

		this.adapter = new EntryAdapter(this, entries);
		this.entriesListView.setAdapter(this.adapter);

		this.adapter.notifyDataSetChanged();

		this.openEntry = this.getOpenEntryFromList(entries);
		this.updateButtons();
		this.calculateTotalDateHours(entries);
	}

	private void listMonthEntries() {

		Calendar firstDate = Calendar.getInstance();
		firstDate.setTime(this.actualDate);
		firstDate.set(Calendar.DAY_OF_MONTH, 1);

		Calendar lastDate = Calendar.getInstance();
		lastDate.setTime(this.actualDate);
		lastDate.set(Calendar.DAY_OF_MONTH, lastDate.getActualMaximum(Calendar.DAY_OF_MONTH));

		List<Entry> entries = this.entryDao.listByPeriod(firstDate.getTime(), lastDate.getTime());
		this.calculateTotalMonthHours(entries);
	}

	private Entry getOpenEntryFromList(List<Entry> entries) {

		if (entries.isEmpty()) {
			return null;
		}

		Entry entry = entries.get(entries.size() - 1);
		if (entry.getCheckOut() != null) {
			return null;
		}
		return entry;
	}

	public void onClickPreviousDay(View view) {

		Calendar calendar = Calendar.getInstance();
		calendar.setTime(this.actualDate);
		calendar.set(DAY_OF_MONTH, calendar.get(DAY_OF_MONTH) - 1);

		this.setActualDate(calendar.getTime());
	}

	public void onClickNextDay(View view) {

		Calendar calendar = Calendar.getInstance();
		calendar.setTime(this.actualDate);
		calendar.set(DAY_OF_MONTH, calendar.get(DAY_OF_MONTH) + 1);

		this.setActualDate(calendar.getTime());
	}

	private void setActualDate(Date date) {

		this.actualDate = date;

		this.setScreenDate();
		this.listEntries();
	}

	public void onClickCheckIn(View view) {

		try {

			Day day = this.dayDao.getByDay(this.actualDate);

			if (day.getId() == Day.UNDEFINED) {
				day.setDay(this.actualDate);
				day = this.dayDao.create(day);
			}

			Entry entry = new Entry();
			entry.setDay(day);
			entry.setCheckIn(this.getTimeUsingActualDate());

			this.entryDao.create(entry);
			this.listEntries();

		} catch (TimeCardException e) {
			Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
		}
	}

	public void onClickCheckOut(View view) {

		try {

			this.openEntry.setCheckOut(this.getTimeUsingActualDate());
			this.entryDao.update(this.openEntry);

			this.listEntries();

		} catch (TimeCardException e) {
			Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
		}
	}

	@Override
	@SuppressWarnings("unchecked")
	public void onEventListener(Event<?> event) {

		Class<? extends Event<?>> eventClass = (Class<? extends Event<?>>) event.getClass();
		if (eventClass == UpdateEntryEvent.class) {
			this.update(event.getSource());
			return;
		}

		if (eventClass == RemoveEntryEvent.class) {
			this.listEntries();
			return;
		}
	}

	private void update(Object object) {

		try {

			this.entryDao.update((Entry) object);
			this.listEntries();

		} catch (TimeCardException e) {
			Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
		}
	}

	public void onClickAddNotes(View view) {

		Day day = this.dayDao.getByDay(this.actualDate);

		if (day.getId() == Day.UNDEFINED) {
			Toast.makeText(this, R.string.noDataFoundForSelectedDay, Toast.LENGTH_LONG).show();
			return;
		}

		new NotesDialog(this.actualDate, this).show();
	}

	public void onClickExport() {
		new ExportDialog(this).show();
	}

	private Date getTimeUsingActualDate() {

		Calendar calendar = Calendar.getInstance();
		calendar.setTime(this.actualDate);

		Date date = new Date();
		Calendar actualCalendar = Calendar.getInstance();
		actualCalendar.setTime(date);

		actualCalendar.set(DAY_OF_MONTH, calendar.get(DAY_OF_MONTH));
		actualCalendar.set(MONTH, calendar.get(MONTH));
		actualCalendar.set(YEAR, calendar.get(YEAR));
		actualCalendar.set(SECOND, 0);
		actualCalendar.set(MILLISECOND, 0);

		return actualCalendar.getTime();
	}

	private void calculateTotalDateHours(List<Entry> entries) {

		int totalMinutes = 0;
		for (Entry entry : entries) {
			totalMinutes += entry.getTotalMinutes();
		}
		Util.setViewText(this, R.id.totalDateHoursText, Util.getTotalHours(totalMinutes));
	}

	private void calculateTotalMonthHours(List<Entry> entries) {

		int totalMinutes = 0;
		for (Entry entry : entries) {
			totalMinutes += entry.getTotalMinutes();
		}
		Util.setViewText(this, R.id.totalMonthHoursText, Util.getTotalHours(totalMinutes));
	}

	private void updateButtons() {

		boolean checkedIn = (this.openEntry != null);
		Util.<Button> getViewById(this, R.id.checkInButton).setEnabled(!checkedIn);
		Util.<Button> getViewById(this, R.id.checkOutButton).setEnabled(checkedIn);
	}
}